/**
 * 2009/12/11
 */
package com.googlecode.derby.customize;

import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import com.googlecode.derby.customize.exception.DataControllerException;

/**
 * Derbyデータコントロールの実装を定義するためのスーパークラス.
 * 
 * @author o.tanaka
 * @version 2009/12/11
 */
public final class DerbyDataController implements DataControllable {

    private static DbInfo derbyInfo;

    private static final Map<String, DbInfo> INFO_MAP =
        new WeakHashMap<String, DbInfo>();

    /**
     * @param aDbName
     *            database name
     * @param user
     *            user
     * @param password
     *            password
     * @param key
     *            key
     * @return DataControllable
     */
    public static DataControllable getInstance(
            final String aDbName,
            final String user,
            final String password,
            final String key) {
        final DataControllable db =
            new DerbyDataController(aDbName, user, password, key);
        return db;
    }

    private DerbyDataController(
            final String aDbName,
            final String user,
            final String password,
            final String key) {
        if (DerbyDataController.derbyInfo != null) {
            if (!DerbyDataController.derbyInfo.getDbname().equals(aDbName)) {
                DerbyDataController.INFO_MAP.put(
                        DerbyDataController.derbyInfo.getDbname(),
                        DerbyDataController.derbyInfo);
                DerbyDataController.derbyInfo = null;
            }
        }
        if (DerbyDataController.derbyInfo == null) {
            DerbyDataController.derbyInfo = DerbyInfo.class.cast(
                    DerbyDataController.INFO_MAP.get(aDbName));
            if (DerbyDataController.derbyInfo == null) {
                DerbyDataController.derbyInfo =
                    new DerbyInfo(aDbName, user, password, key);
            }
        }
        DerbyDataController.derbyInfo.connection();
            DerbyDataController.INFO_MAP.put(
                    aDbName, DerbyDataController.derbyInfo);
    }

    /**
     *
     */
    public void commit() {
        for (final DbInfo aDerbyInfo : DerbyDataController.INFO_MAP.values()) {
            aDerbyInfo.commit();
        }
        if (DerbyDataController.derbyInfo != null) {
            DerbyDataController.derbyInfo.commit();
        }
        this.disConnection();
    }

    /**
     * @param aList data class
     * @return DataControllable
     */
    public DataControllable delete(final List<?> aList) {
        if (DerbyDataController.derbyInfo.isConnectionClose()) {
            DerbyDataController.derbyInfo.connection();
        }
        DerbyDataController.derbyInfo.delete(aList);
        if (DerbyDataController.derbyInfo.getAutoCommit()) {
            this.disConnection();
        }
        return this;
    }

    /**
     * Connection.closeメソッド.
     */
    public void disConnection() {
        for (final DbInfo aDerbyInfo : DerbyDataController.INFO_MAP.values()) {
            if (!aDerbyInfo.isConnectionClose()) {
                aDerbyInfo.close();
            }
        }
        if (DerbyDataController.derbyInfo != null) {
            if (!DerbyDataController.derbyInfo.isConnectionClose()) {
                DerbyDataController.derbyInfo.close();
            }
        }
    }

    /**
     * @return update count
     */
    public long getExecuteUpdateCnt() {
        if (DerbyDataController.derbyInfo.getAutoCommit()) {
            this.disConnection();
        }
        return DerbyDataController.derbyInfo.getExecuteUpdateCnt();
    }

    /**
     * @return List<ResultData>
     */
    public List<ResultData> getResultDatas() {
        final List<ResultData> list =
            DerbyDataController.derbyInfo.getResultDatas();
        if (DerbyDataController.derbyInfo.getAutoCommit()) {
            this.disConnection();
        }
        return list;
    }

    /**
     * @param aMaxCount
     *            最大件数
     * @return List<ResultData>
     */
    public List<ResultData> getResultDatas(final long aMaxCount) {
        final List<ResultData> list =
            DerbyDataController.derbyInfo.getResultDatas(aMaxCount);
        if (DerbyDataController.derbyInfo.getAutoCommit()) {
            this.disConnection();
        }
        return list;
    }

    /**
     * @param aBgnRownum
     *            開始件数
     * @param aEndRownum
     *            終了件数
     * @return List<ResultData>
     */
    public List<ResultData> getResultDatas(
            final long aBgnRownum, final long aEndRownum) {
        final List<ResultData> list =
            DerbyDataController.derbyInfo.getResultDatas(aBgnRownum,
                    aEndRownum);
        if (DerbyDataController.derbyInfo.getAutoCommit()) {
            this.disConnection();
        }
        return list;
    }

    /**
     * @param aList
     *            data class
     * @return DataControllable
     */
    public DataControllable insertUpdate(final List<?> aList) {
        return this.insertUpdate(aList, null);
    }

    /**
     * @param aList
     *            data class
     * @param aNotOverride
     *            DbConst.NOT_OVERRIDE
     * @return DataControllable
     */
    public DataControllable insertUpdate(
            final List<?> aList, final String aNotOverride) {
        if (DerbyDataController.derbyInfo.isConnectionClose()) {
            DerbyDataController.derbyInfo.connection();
        }
        DerbyDataController.derbyInfo.setBindData(null);
        DerbyDataController.derbyInfo.insertUpdate(aList, aNotOverride);
        if (DerbyDataController.derbyInfo.getAutoCommit()) {
            this.disConnection();
        }
        return this;
    }

    /**
     * @return create table is true.
     */
    public boolean isCreateTable() {
        return DerbyDataController.derbyInfo.isCreateTable();
    }

    /**
     * @param aColumn
     *            order by column
     * @return DataControllable
     */
    public DataControllable orderby(final String aColumn) {
        DerbyDataController.derbyInfo.orderby(aColumn);
        return this;
    }

    /**
     */
    public void rollback() {
        for (final DbInfo aDerbyInfo : DerbyDataController.INFO_MAP.values()) {
            aDerbyInfo.rollback();
        }
        if (DerbyDataController.derbyInfo != null) {
            DerbyDataController.derbyInfo.rollback();
        }
        this.disConnection();
    }

    /**
     * @param aClass
     *            table class
     * @return DataControllable
     */
    public DataControllable select(final Class<?> aClass) {
        return this.select(aClass, null);
    }

    /**
     * @param aClass
     *            table class
     * @param aObject
     *            bind class
     * @return DataControllable
     */
    public DataControllable select(
            final Class<?> aClass,
            final Object aObject) {
        if (DerbyDataController.derbyInfo.isConnectionClose()) {
            DerbyDataController.derbyInfo.connection();
        }
        this.table(aClass);
        DerbyDataController.derbyInfo.setBindData(null);
        DerbyDataController.derbyInfo.select(aObject);
        return this;
    }

    /**
     * @param aObject
     *            bind class
     * @return DataControllable
     */
    public DataControllable select(final Object aObject) {
        if (DerbyDataController.derbyInfo != null) {
            DerbyDataController.derbyInfo.setBindData(null);
            DerbyDataController.derbyInfo.select(aObject);
            return this;
        }
        throw new DataControllerException(
                "select error:",
                this, new NullPointerException());
    }

    /**
     * @param aClass
     * @return DataControllable
     */
    @Override
    public DataControllable selectTables(final Class<?>... aClass) {
        if (DerbyDataController.derbyInfo.isConnectionClose()) {
            DerbyDataController.derbyInfo.connection();
        }
        this.tables(aClass);
        DerbyDataController.derbyInfo.setBindData(null);
        DerbyDataController.derbyInfo.selects();
        return this;
    }

    /**
     * setAutoCommitメソッド.
     * 
     * @param b
     *            boolean
     * @return DataControllable
     */
    public DataControllable setAutoCommit(final boolean b) {
        for (final DbInfo aDerbyInfo : DerbyDataController.INFO_MAP.values()) {
            aDerbyInfo.setAutoCommit(b);
            DerbyDataController.INFO_MAP
                    .put(aDerbyInfo.getDbname(), aDerbyInfo);
        }
        if (DerbyDataController.derbyInfo != null) {
            DerbyDataController.derbyInfo.setAutoCommit(b);
        }
        return this;
    }

    /**
     * @param aB
     *            true/false
     */
    public void setReadOnly(final boolean aB) {
        DerbyDataController.derbyInfo.setReadOnly(aB);
    }

    /**
     * @param aClass
     *            table entity class
     * @return DataControllable
     */
    public DataControllable table(final Class<?> aClass) {
        if (DerbyDataController.derbyInfo != null) {
            if (DerbyDataController.derbyInfo.isConnectionClose()) {
                DerbyDataController.derbyInfo.connection();
            }
            DerbyDataController.derbyInfo.table(aClass);
        } else {
            throw new DataControllerException(
                    "table error:",
                    this, new NullPointerException());
        }
        return this;
    }

    /**
     * @param aClass
     */
    private DataControllable tables(final Class<?>... aClass) {
        if (DerbyDataController.derbyInfo != null) {
            if (DerbyDataController.derbyInfo.isConnectionClose()) {
                DerbyDataController.derbyInfo.connection();
            }
            DerbyDataController.derbyInfo.tables(aClass);
        } else {
            throw new DataControllerException(
                    "table error:",
                    this, new NullPointerException());
        }
        return this;
    }

    /**
     * @param aSql
     *            sql
     * @param aObjects
     *            data class
     * @return DataControllable
     */
    public DataControllable where(
            final String aSql,
            final Object... aObjects) {
        DerbyDataController.derbyInfo.setQuery(null);
        DerbyDataController.derbyInfo.where(aSql, aObjects);
        return this;
    }

}
